package com.kun.controller;

import com.kun.entity.SysPermission;
import com.kun.entity.SysUser;
import com.kun.remote.RemoteUserService;
import com.kun.util.BuildKeyUtil;
import com.kun.util.ResponseResult;
import com.kun.util.SpringContextHolder;
import com.nimbusds.jose.shaded.gson.internal.LinkedTreeMap;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.LogoutSuccessEvent;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.authorization.*;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import java.security.Principal;
import java.time.Instant;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author kun.li
 */
@RestController
@RequestMapping("token")
@RequiredArgsConstructor
@Slf4j
public class TokenController {
    private final RegisteredClientRepository registeredClientRepository;

    private final OAuth2AuthorizationConsentService authorizationConsentService;

    private final OAuth2AuthorizationService authorizationService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;


    /**
     * 登录页面
     *
     * @return 登录页面路径
     */
    @GetMapping("/login")
    public ModelAndView login(ModelAndView modelAndView, @RequestParam(required = false) String error) {
        modelAndView.setViewName("login");
        modelAndView.addObject("error", error);
        return modelAndView;
    }

    /**
     * 授权同意页面
     */
    @GetMapping(value = "/consent")
    public ModelAndView consent(Principal principal, ModelAndView model,
                                @RequestParam(OAuth2ParameterNames.CLIENT_ID) String clientId,
                                @RequestParam(OAuth2ParameterNames.SCOPE) String scope,
                                @RequestParam(OAuth2ParameterNames.STATE) String state,
                                @RequestParam(name = OAuth2ParameterNames.USER_CODE, required = false) String userCode) {

        // Remove scopes that were already approved
        Set<String> scopesToApprove = new HashSet<>();
        Set<String> previouslyApprovedScopes = new HashSet<>();
        RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);
        if (registeredClient == null) {
            throw new RuntimeException("客户端不存在");
        }
        OAuth2AuthorizationConsent currentAuthorizationConsent =
                this.authorizationConsentService.findById(registeredClient.getId(), principal.getName());
        Set<String> authorizedScopes;
        if (currentAuthorizationConsent != null) {
            authorizedScopes = currentAuthorizationConsent.getScopes();
        } else {
            authorizedScopes = Collections.emptySet();
        }
        for (String requestedScope : StringUtils.delimitedListToStringArray(scope, " ")) {
            if (OidcScopes.OPENID.equals(requestedScope)) {
                continue;
            }
            if (authorizedScopes.contains(requestedScope)) {
                previouslyApprovedScopes.add(requestedScope);
            } else {
                scopesToApprove.add(requestedScope);
            }
        }

        model.addObject("clientId", clientId);
        model.addObject("state", state);
        model.addObject("scopes", scopesToApprove);
        model.addObject("previouslyApprovedScopes", previouslyApprovedScopes);
        model.addObject("principalName", principal.getName());
        model.addObject("userCode", userCode);
        if (StringUtils.hasText(userCode)) {
            model.addObject("requestURI", "/oauth2/device_verification");
        } else {
            model.addObject("requestURI", "/oauth2/authorize");
        }
        model.setViewName("confirm");
        return model;
    }


    /**
     * 校验token 是否存在或者过期
     */
    @GetMapping("checkToken")
    public ResponseResult checkToken(String token) {
        OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
        if (authorization == null) {
            return ResponseResult.of(true, 500, "处理失败", false);
        }
        OAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getAccessToken();
        if (accessToken == null || !StringUtils.hasText(accessToken.getToken().getTokenValue())) {
            return ResponseResult.of(true, 500, "处理失败", false);
        }
        OAuth2AccessToken accessTokenToken = accessToken.getToken();
        Instant expiresAt = accessTokenToken.getExpiresAt();
        return ResponseResult.success(expiresAt.toEpochMilli());
    }

    /**
     * 移除token
     *
     * @param token
     * @return
     */
    @DeleteMapping("{token}")
    public ResponseResult<Boolean> removeToken(@PathVariable("token") String token) {
        log.info("【{}】", "开始移除token");
        OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
        if (authorization == null) {
            return ResponseResult.failed();
        }
        OAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getAccessToken();
        if (accessToken == null || !StringUtils.hasText(accessToken.getToken().getTokenValue())) {
            return ResponseResult.failed();
        }
        // 移除用户信息和权限信息
        UsernamePasswordAuthenticationToken attribute = authorization.getAttribute(Principal.class.getName());
        String principal = (String) attribute.getPrincipal();
        redisTemplate.delete(principal);
        redisTemplate.delete(BuildKeyUtil.buildKey(principal));
        // 开始从认证服务中移除token
        authorizationService.remove(authorization);
        SpringContextHolder.publishEvent(new LogoutSuccessEvent(new PreAuthenticatedAuthenticationToken(
                authorization.getPrincipalName(), authorization.getRegisteredClientId())));
        log.info("【{}】", "token 移除成功");
        return ResponseResult.success(true);
    }

    @Autowired
    private RemoteUserService remoteUserService;


    @GetMapping("getUserInfo")
    public ResponseResult<SysUser> getUserInfo() {
        JwtAuthenticationToken authentication = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LinkedTreeMap<String, Object> userInfo = ((Jwt) authentication.getPrincipal()).getClaim("userInfo");
        String userId = (String) userInfo.get("userId");
        String tenantId = (String) userInfo.get("tenantId");
        String username = (String) userInfo.get("username");
        SysUser sysUser = remoteUserService.getSysUser(tenantId, username, userId);
        List<SysPermission> sysPermissionList = remoteUserService.selectPermission(tenantId, sysUser.getUserId());
        List<String> btnPermissions = sysPermissionList.stream().map(SysPermission::getBtnPermission).toList();
        sysUser.setBtnPermission(btnPermissions);
        return ResponseResult.success(sysUser);
    }

}
